package storage

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io"
	"path/filepath"
	"strings"
	"time"

	"github.com/qiniu/go-sdk/v7/auth/qbox"
	"github.com/qiniu/go-sdk/v7/storage"
)

// QiniuConfig 七牛云存储配置
type QiniuConfig struct {
	AccessKey   string // 访问密钥
	SecretKey   string // 密钥
	Bucket      string // 存储空间名称
	Domain      string // 域名
	PathPrefix  string // 路径前缀（可选）
	IsPublic    bool   // 是否公开访问
	Timeout     int    // 超时时间（秒）
	MaxFileSize int64  // 最大文件大小（字节）
	UseHTTPS    bool   // 是否使用HTTPS
}

// QiniuStorage 七牛云存储实现
type QiniuStorage struct {
	config       *QiniuConfig
	mac          *qbox.Mac
	bucketMgr    *storage.BucketManager
	formUploader *storage.FormUploader
	baseURL      string
}

// NewQiniuStorage 创建七牛云存储
func NewQiniuStorage(config *QiniuConfig) (Storage, error) {
	if config == nil {
		return nil, errors.New("七牛云配置不能为空")
	}

	if config.AccessKey == "" || config.SecretKey == "" {
		return nil, errors.New("七牛云访问密钥和密钥不能为空")
	}

	if config.Bucket == "" {
		return nil, errors.New("七牛云存储空间名称不能为空")
	}

	if config.Domain == "" {
		return nil, errors.New("七牛云域名不能为空")
	}

	// 创建鉴权对象
	mac := qbox.NewMac(config.AccessKey, config.SecretKey)

	// 构建存储配置
	cfg := storage.Config{
		UseHTTPS:      config.UseHTTPS,
		UseCdnDomains: false,
	}

	// 创建存储空间管理器
	bucketMgr := storage.NewBucketManager(mac, &cfg)

	// 创建表单上传器
	formUploader := storage.NewFormUploader(&cfg)

	// 构建基础URL
	baseURL := config.Domain
	if !strings.HasPrefix(baseURL, "http://") && !strings.HasPrefix(baseURL, "https://") {
		if config.UseHTTPS {
			baseURL = "https://" + baseURL
		} else {
			baseURL = "http://" + baseURL
		}
	}

	return &QiniuStorage{
		config:       config,
		mac:          mac,
		bucketMgr:    bucketMgr,
		formUploader: formUploader,
		baseURL:      baseURL,
	}, nil
}

// Upload 上传文件
func (s *QiniuStorage) Upload(ctx context.Context, name string, reader io.Reader, option *UploadOption) (*FileInfo, error) {
	// 如果有路径前缀，则添加到文件路径前
	filePath := name
	if s.config.PathPrefix != "" {
		filePath = filepath.Join(s.config.PathPrefix, filePath)
	}

	// 规范化文件路径，使用正斜杠
	filePath = strings.ReplaceAll(filePath, "\\", "/")

	// 创建上传策略
	putPolicy := storage.PutPolicy{
		Scope: s.config.Bucket,
	}
	upToken := putPolicy.UploadToken(s.mac)

	// 创建上传表单
	ret := storage.PutRet{}
	putExtra := storage.PutExtra{}

	// 设置内容类型
	if option != nil && option.ContentType != "" {
		putExtra.MimeType = option.ContentType
	}

	// 读取内容到内存
	data, err := io.ReadAll(reader)
	if err != nil {
		return nil, fmt.Errorf("读取上传内容失败: %w", err)
	}

	// 上传文件
	err = s.formUploader.Put(ctx, &ret, upToken, filePath, bytes.NewReader(data), int64(len(data)), &putExtra)
	if err != nil {
		return nil, fmt.Errorf("上传文件到七牛云失败: %w", err)
	}

	// 获取文件URL
	url, err := s.GetURL(ctx, filePath, 0)
	if err != nil {
		return nil, err
	}

	// 返回文件信息
	return &FileInfo{
		Name:         filepath.Base(name),
		Path:         filePath,
		URL:          url,
		Size:         int64(len(data)),
		ContentType:  putExtra.MimeType,
		LastModified: time.Now(),
	}, nil
}

// Delete 删除文件
func (s *QiniuStorage) Delete(ctx context.Context, path string) error {
	// 如果有路径前缀，则添加到文件路径前
	filePath := path
	if s.config.PathPrefix != "" {
		filePath = filepath.Join(s.config.PathPrefix, filePath)
	}

	// 规范化文件路径，使用正斜杠
	filePath = strings.ReplaceAll(filePath, "\\", "/")

	// 删除文件
	err := s.bucketMgr.Delete(s.config.Bucket, filePath)
	if err != nil {
		return fmt.Errorf("删除七牛云文件失败: %w", err)
	}

	return nil
}

// GetURL 获取文件URL
func (s *QiniuStorage) GetURL(ctx context.Context, path string, expiration time.Duration) (string, error) {
	// 如果有路径前缀，则添加到文件路径前
	filePath := path
	if s.config.PathPrefix != "" && !strings.HasPrefix(filePath, s.config.PathPrefix) {
		filePath = filepath.Join(s.config.PathPrefix, filePath)
	}

	// 规范化文件路径，使用正斜杠
	filePath = strings.ReplaceAll(filePath, "\\", "/")

	// 构建文件URL
	fileURL := fmt.Sprintf("%s/%s", strings.TrimRight(s.baseURL, "/"), strings.TrimLeft(filePath, "/"))

	// 如果是私有访问，则生成带签名的URL
	if !s.config.IsPublic {
		if expiration <= 0 {
			expiration = time.Hour // 默认1小时有效期
		}
		deadline := time.Now().Add(expiration).Unix()
		privateURL := storage.MakePrivateURL(s.mac, s.baseURL, filePath, deadline)
		return privateURL, nil
	}

	return fileURL, nil
}

// GetFileInfo 获取文件信息
func (s *QiniuStorage) GetFileInfo(ctx context.Context, path string) (*FileInfo, error) {
	// 如果有路径前缀，则添加到文件路径前
	filePath := path
	if s.config.PathPrefix != "" {
		filePath = filepath.Join(s.config.PathPrefix, filePath)
	}

	// 规范化文件路径，使用正斜杠
	filePath = strings.ReplaceAll(filePath, "\\", "/")

	// 获取文件信息
	fileInfo, err := s.bucketMgr.Stat(s.config.Bucket, filePath)
	if err != nil {
		return nil, fmt.Errorf("获取七牛云文件信息失败: %w", err)
	}

	// 获取文件URL
	url, err := s.GetURL(ctx, filePath, 0)
	if err != nil {
		return nil, err
	}

	// 返回文件信息
	return &FileInfo{
		Name:         filepath.Base(path),
		Path:         filePath,
		URL:          url,
		Size:         fileInfo.Fsize,
		ContentType:  fileInfo.MimeType,
		LastModified: time.Unix(fileInfo.PutTime/10000000, 0),
	}, nil
}

// ListFiles 列出指定目录下的文件
func (s *QiniuStorage) ListFiles(ctx context.Context, prefix string) ([]*FileInfo, error) {
	// 如果有路径前缀，则添加到文件路径前
	if s.config.PathPrefix != "" {
		prefix = filepath.Join(s.config.PathPrefix, prefix)
	}

	// 规范化文件路径，使用正斜杠
	prefix = strings.ReplaceAll(prefix, "\\", "/")

	// 列出文件
	limit := 1000
	delimiter := ""
	marker := ""

	var allFiles []storage.ListItem
	for {
		entries, _, nextMarker, hasNext, err := s.bucketMgr.ListFiles(s.config.Bucket, prefix, delimiter, marker, limit)
		if err != nil {
			return nil, fmt.Errorf("列出七牛云文件失败: %w", err)
		}

		allFiles = append(allFiles, entries...)

		if !hasNext {
			break
		}
		marker = nextMarker
	}

	// 处理结果
	files := make([]*FileInfo, 0, len(allFiles))
	for _, item := range allFiles {
		// 跳过目录
		if strings.HasSuffix(item.Key, "/") {
			continue
		}

		// 获取文件URL
		url, err := s.GetURL(ctx, item.Key, 0)
		if err != nil {
			continue
		}

		// 添加文件信息
		files = append(files, &FileInfo{
			Name:         filepath.Base(item.Key),
			Path:         item.Key,
			URL:          url,
			Size:         item.Fsize,
			ContentType:  item.MimeType,
			LastModified: time.Unix(item.PutTime/10000000, 0),
		})
	}

	return files, nil
}

// Type 获取存储类型
func (s *QiniuStorage) Type() StorageType {
	return StorageTypeQiniu
}
